#include "RenderStates.h"

ID3D11RasterizerState* RenderStates::noCullRS = 0;
ID3D11BlendState* RenderStates::transparentBS = 0;
ID3D11BlendState* RenderStates::alphaToCoverageBillboardBS = 0;
ID3D11RasterizerState* RenderStates::wireframeRS = 0;
ID3D11RasterizerState* RenderStates::wireframeNoCullRS = 0;
ID3D11DepthStencilState* RenderStates::lessEqualDepthTestDSS = 0;
ID3D11BlendState* RenderStates::additiveBlendingBS = 0;
ID3D11DepthStencilState* RenderStates::equalsDepthTestDSS = 0;
ID3D11DepthStencilState* RenderStates::lessThanNoDepthWritesDSS = 0;
ID3D11BlendState* RenderStates::trans2DBS = 0;

void RenderStates::InitAll(ID3D11Device* device)
{
	//Ensure that we init our render states ONCE.
	static bool init = false;

	if (!init)
	{
		InitRasterizerStates(device);
		InitBlendStates(device);
		InitDepthStencilStates(device);
		
		init = true;
	}
}

void RenderStates::DestroyAll()
{
	ReleaseCOM(noCullRS);
	ReleaseCOM(transparentBS);
	ReleaseCOM(alphaToCoverageBillboardBS);
	ReleaseCOM(wireframeRS);
	ReleaseCOM(wireframeNoCullRS);
	ReleaseCOM(lessEqualDepthTestDSS);
	ReleaseCOM(additiveBlendingBS);
	ReleaseCOM(trans2DBS);

	//Release states for tile forward rendering engine
	ReleaseCOM(equalsDepthTestDSS);
	ReleaseCOM(lessThanNoDepthWritesDSS);
}

void RenderStates::InitRasterizerStates(ID3D11Device* device)
{
	D3D11_RASTERIZER_DESC rd;

	//noCullRS - turns back face culling off only
	ZeroMemory(&rd, sizeof(D3D11_RASTERIZER_DESC));
	rd.FillMode = D3D11_FILL_SOLID;
	rd.CullMode = D3D11_CULL_NONE;
	rd.FrontCounterClockwise = false;
	rd.DepthClipEnable = true;
	HR(device->CreateRasterizerState(&rd, &noCullRS));

	//Renders in wireframe mode.
	ZeroMemory(&rd, sizeof(D3D11_RASTERIZER_DESC));
	rd.FillMode = D3D11_FILL_WIREFRAME;
	rd.CullMode = D3D11_CULL_BACK;
	rd.FrontCounterClockwise = false;
	rd.DepthClipEnable = true;
	HR(device->CreateRasterizerState(&rd, &wireframeRS));

	//Renders in wireframe mode NO backface culling. 
	rd.CullMode = D3D11_CULL_NONE;
	HR(device->CreateRasterizerState(&rd, &wireframeNoCullRS));
}


void RenderStates::InitBlendStates(ID3D11Device* device)
{
	D3D11_BLEND_DESC bd;

	//transparentBS - a good standard blend state when we want
	//transparancy (eg glass and water)
	ZeroMemory(&bd, sizeof(D3D11_BLEND_DESC));
	bd.AlphaToCoverageEnable = false;
	bd.IndependentBlendEnable = false;
	bd.RenderTarget[0].BlendEnable = true;
	bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	bd.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	bd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	bd.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	HR(device->CreateBlendState(&bd, &transparentBS));

	//alphaToCoverageBillboardBS - Enable alpha to coverage.
	ZeroMemory(&bd, sizeof(D3D11_BLEND_DESC));
	bd.AlphaToCoverageEnable = true;
	bd.IndependentBlendEnable = false;
	bd.RenderTarget[0].BlendEnable = false;
	bd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	HR(device->CreateBlendState(&bd, &alphaToCoverageBillboardBS));

	//additiveBlendingBS - Enable Additive Blending. 
	ZeroMemory(&bd, sizeof(D3D11_BLEND_DESC));
	bd.AlphaToCoverageEnable = false;
	bd.IndependentBlendEnable = false;
	bd.RenderTarget[0].BlendEnable = true;
	bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	bd.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
	bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
	bd.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
	bd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	HR(device->CreateBlendState(&bd, &additiveBlendingBS));

	ZeroMemory(&bd, sizeof(D3D11_BLEND_DESC));
	bd.AlphaToCoverageEnable = false;
	bd.IndependentBlendEnable = false;
	bd.RenderTarget[0].BlendEnable = true;
	bd.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	bd.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	bd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	HR(device->CreateBlendState(&bd, &trans2DBS));

}

void RenderStates::InitDepthStencilStates(ID3D11Device* device)
{
	D3D11_DEPTH_STENCIL_DESC dsd;

	//disableDepthTestDSS - Disables Depth Test (strictly, it ensures that the depth
	//test ALWAYS passes)
	ZeroMemory(&dsd, sizeof(D3D11_DEPTH_STENCIL_DESC));
	dsd.DepthEnable = true;
	dsd.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
	dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	dsd.StencilEnable = false;
	dsd.StencilReadMask = 0xff;
	dsd.StencilWriteMask = 0xff;
	dsd.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
	dsd.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	dsd.FrontFace.StencilFunc = D3D11_COMPARISON_LESS_EQUAL;
	dsd.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	dsd.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
	dsd.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	dsd.BackFace.StencilFunc = D3D11_COMPARISON_LESS_EQUAL;
	dsd.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	HR(device->CreateDepthStencilState(&dsd, &lessEqualDepthTestDSS));

	//equalsDepthTestDSS - Changes the depth test from < to ==.
	ZeroMemory(&dsd, sizeof(D3D11_DEPTH_STENCIL_DESC));
	dsd.DepthEnable = true;
	dsd.DepthFunc = D3D11_COMPARISON_EQUAL;
	dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; //No need to have depth writes.
	dsd.StencilEnable = false;
	//Create
	HR(device->CreateDepthStencilState(&dsd, &equalsDepthTestDSS));

	//lessThanNoDepthWritesDSS - < depth test with no depth writes.
	ZeroMemory(&dsd, sizeof(D3D11_DEPTH_STENCIL_DESC));
	dsd.DepthEnable = true;
	dsd.StencilEnable = false;
	dsd.DepthFunc = D3D11_COMPARISON_LESS;
	dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
	//Create
	HR(device->CreateDepthStencilState(&dsd, &lessThanNoDepthWritesDSS));
}